iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 22
2
Modern Web

React 30天系列 第 22

Day 22-使用Query Parameters提供todos的過濾條件吧

  • 分享至 

  • xImage
  •  

前情提要:昨天繼續擴增todos後,已經可以好好管理非預期的路由了,不管是走錯地方的需要Redirect或是根本沒那頁的404,都可以好好處理它們。今天就來看看Query Parameters吧!

Query Parameters其實就是指window.location.search,也就是會出現在網址列的參數(如下圖問號(?)後的部分)。
https://ithelp.ithome.com.tw/upload/images/20181029/201115959J4kVdT6eN.png

這些參數可以指引我們如何處理資料,所以今天的目標就是做todos的過濾!

目標說明

  1. 使用Query Parameters篩選todos,預計增加ative和completed兩種條件篩選,加上原本的todos顯示全部,有三種可能狀態!
    • All - path不變,依然是原本的"/todos"
    • Active - 參數state,value為active
    • Completed - 參數state,value為completed
  2. 加映,未完成的待辦事項"active"筆數顯示

以結果來看的話就是增加下圖紅框那塊
https://ithelp.ithome.com.tw/upload/images/20181029/20111595bp2JgAT4K2.png

使用<Link> component設定search Value

之前Link的操作,都是很簡單的在to屬性設定string來指定要連接的位置,如下:

<Link to="/about">About</Link>

其實to也接受object,在object內有四個屬性可以用,分別為:

  • pathname: 代表要連結的路徑。如:"/about"
  • search: 代表要查詢的參數。如:"?state=active"
  • hash: 代表要放在URL後的hash值。如:"a-hash"
  • state: 代表要存留的狀態,不會在網址列出現,但可以捎訊息到新的location

今天會需要的對象是search,除了state之外的屬性,如果不想把它拆開設定在object裡,其實也可以跟串燒一樣把全部的內容組成字串丟給to就好了,如下:

<Link to="/todos?state=active#123">Active</Link

為了維護整潔的空間,我還是使用object設定好了~!

我新增了一個TodoFilterConatiner.js檔案,處理步驟如下:

  1. 從react-router-dom匯入Link
  2. 因為不想重複寫li和Link等設定,所以把每個連結所需的資訊放在filterInfo用array存起來。
  3. 使用connect取得store內todoList的資料,並設定當todoList的資料大於一筆時才會顯示這個component。
  4. 使用filter將篩選completed為false的待辦清單筆數。
import React from "react";
import { Link } from "react-router-dom";
import { connect } from "react-redux";

const filterInfo = [
  {to: {pathname: "/todos"}, text: "All"},
  {to: {search: "?state=active"}, text: "Active"},
  {to: {search: "?state=completed"}, text: "Completed"}
]

const TodoFilterConatiner = ({todoLength, activeLength}) => todoLength ? (
  <div className="state">
    <span>{activeLength} 項未完成</span>
    <ul className="state-filter-list">
      { filterInfo.map(({to, text}, index) => (
        <li key={index}>
          <Link to={to} className="state-filter-item">{text}</Link>
        </li>
      ))}
    </ul>
  </div>
) : null;

export default connect(({todoList}) => ({
  todoLength: todoList.length,
  activeLength: todoList.filter(({completed}) => !completed).length
}))(TodoFilterConatiner);

處理完search後,順利的話可以看到點擊anchor後,網址列尾端會帶上我們預期的參數state,如下:
https://ithelp.ithome.com.tw/upload/images/20181029/201115959J4kVdT6eN.png
接著,我們就要回到TodoListContainer.js過濾我們的條件啦

使用URLSearchParams Web API取得參數value

這部分會看到幾個之前沒提過的新面孔,withRouter和compose。

  • withRouter: 來自react-router-dom,是個HOC,透過它可在prop取得history、location和match等訊息。詳細內容請參閱withRouter
    https://ithelp.ithome.com.tw/upload/images/20181029/20111595rMMYKruuyR.png
  • compose: 來自redux,由右到左組合function。這是functional programming的實用方法,因為很幫變所以被包在redux裡。

總而言之就是使用compose組合越來越多的high-order components,並使用withRouter取得router訊息,主要處理資料的地方在mapStateToProps,說明如下:

  1. mapStateToProps的第一個參數是store內的state資料,透過解構取得todoList
  2. mapStateToProps的第二個參數是props資料,透過解構取得withRouter送來的location內的search value
  3. 使用URLSearchParams取得search參數,並使用String.prototype.substring()
    只保留問號(?)後的string,如:state=active或state=completed。
    ※備註:部分瀏覽器不支援URLSearchParams,如果想解決此問題可使用第三方library幫忙解除困擾,如:query-string

取得state後就可以做completed判斷,就可以使用filter來過濾todoList了。

import { compose } from 'redux';
import { withRouter } from 'react-router-dom';

// ... TodoListContainer component

const mapStateToProps = ({todoList}, {location:{search}}) => {
  let params = new URLSearchParams(search.substring(1));
  let state = params.get('state');
  let completed = state === "completed";
  let todo = state ? todoList.filter(item => item.completed === completed) : todoList;
  return { todo };
}

export default compose(
  withRouter,
  connect(mapStateToProps, {
    completeTodo, removeTodo
  })
)(TodoListContainer);

最後,記得在Todo.js匯入這次新增的component就可以完成並測試了。
實際操作畫面如下:
https://i.imgur.com/fRnINik.gif
github傳送門


今日總結:todos這邊暫時玩得差不多了,明天往news那邊去開墾吧!


上一篇
Day 21-這裡不能來,閃閃去旁邊(Redirect)與No Match (404)
下一篇
Day 23-使用redux-thunk處理News API
系列文
React 30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言